@vyriy/render 0.4.10 → 0.4.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -88,6 +88,41 @@ customElement({
88
88
  });
89
89
  ```
90
90
 
91
+ ## Custom element SSR hydration
92
+
93
+ For SSR custom elements, render declarative shadow DOM with a stable mount node marked by `data-vyriy-root`.
94
+ The `rendered` attribute tells `customElement` to hydrate the existing shadow DOM instead of creating a new client container.
95
+
96
+ ```html
97
+ <vyriy-profile-card rendered name="Ada">
98
+ <template shadowrootmode="open">
99
+ <link rel="stylesheet" href="/main.css" />
100
+ <div data-vyriy-root>
101
+ <!-- React SSR markup -->
102
+ </div>
103
+ </template>
104
+ </vyriy-profile-card>
105
+ ```
106
+
107
+ On the client, use the same registration. When the browser has already created `shadowRoot` from the template, `customElement` reuses it and calls `hydrateRoot` with `[data-vyriy-root]`.
108
+
109
+ ```tsx
110
+ customElement({
111
+ tag: 'vyriy-profile-card',
112
+ elements: () => {
113
+ const container = document.createElement('div');
114
+
115
+ return {
116
+ elements: [container],
117
+ root: container,
118
+ };
119
+ },
120
+ render: (customElement) => {
121
+ return <ProfileCard name={customElement.getAttribute('name') ?? ''} />;
122
+ },
123
+ });
124
+ ```
125
+
91
126
  ## HTML string
92
127
 
93
128
  ```tsx
@@ -111,7 +146,7 @@ const htmlStream = await stream({
111
146
  });
112
147
  ```
113
148
 
114
- ## Rrerender
149
+ ## Prerender
115
150
 
116
151
  ```tsx
117
152
  import { prerender } from '@vyriy/render';
package/custom-element.js CHANGED
@@ -1,5 +1,16 @@
1
1
  import { createRoot, hydrateRoot } from 'react-dom/client';
2
2
  import { createMissingCustomElementRootError } from './errors.js';
3
+ const rootSelector = '[data-vyriy-root]';
4
+ const fallbackRootSelector = ':not(link, style, template)';
5
+ const findCustomElementRoot = (shadow) => {
6
+ return shadow.querySelector(rootSelector) ?? shadow.querySelector(fallbackRootSelector);
7
+ };
8
+ const requireCustomElementRoot = (root) => {
9
+ if (!root) {
10
+ throw createMissingCustomElementRootError();
11
+ }
12
+ return root;
13
+ };
3
14
  export const customElement = ({ tag, mode = 'open', renderedAttribute = 'rendered', elements, render, options = {}, }) => {
4
15
  if (customElements.get(tag)) {
5
16
  return;
@@ -9,12 +20,15 @@ export const customElement = ({ tag, mode = 'open', renderedAttribute = 'rendere
9
20
  #mount;
10
21
  constructor() {
11
22
  super();
12
- const shadow = this.attachShadow({ mode });
13
- const renderElements = elements(this);
14
- if (!renderElements.root) {
15
- throw createMissingCustomElementRootError();
23
+ const existingShadow = this.shadowRoot;
24
+ const shadow = existingShadow ?? this.attachShadow({ mode });
25
+ const isHydratableShadow = this.hasAttribute(renderedAttribute) && existingShadow;
26
+ if (isHydratableShadow) {
27
+ this.#mount = requireCustomElementRoot(findCustomElementRoot(existingShadow));
28
+ return;
16
29
  }
17
- this.#mount = renderElements.root;
30
+ const renderElements = elements(this);
31
+ this.#mount = requireCustomElementRoot(renderElements.root);
18
32
  shadow.append(...renderElements.elements);
19
33
  }
20
34
  connectedCallback() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vyriy/render",
3
- "version": "0.4.10",
3
+ "version": "0.4.11",
4
4
  "description": "React rendering adapters for Vyriy projects",
5
5
  "type": "module",
6
6
  "dependencies": {