@lwrjs/lwc-ssr 0.20.1 → 0.20.2

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
@@ -18,6 +18,7 @@
18
18
  - [Islands](#islands)
19
19
  - [Client hydration](#client-hydration)
20
20
  - [Skip SSR](#skip-ssr)
21
+ - [Hydrate on visible](#hydrate-on-visible)
21
22
  - [Routing](#routing)
22
23
  - [Limitations](#limitations)
23
24
  - [Portability](#portability)
@@ -326,6 +327,39 @@ Root components can skip SSR by setting the `lwr:hydrate` directive to `client-o
326
327
 
327
328
  Root components which opt-out of SSR will be fully rendered on the client using the [LWC `createElement()` API](https://www.npmjs.com/package/@lwc/engine-dom).
328
329
 
330
+ ### Hydrate on visible
331
+
332
+ Root components can defer hydration until they become visible in the viewport by setting the `lwr:hydrate` directive to `visible`. This creates a performance optimization where components are only hydrated when they're about to be seen by the user:
333
+
334
+ ```html
335
+ <!-- my-app/src/content/page.html (contentTemplate) -->
336
+
337
+ <!-- hydrated immediately -->
338
+ <my-header lwr:hydrate></my-header>
339
+
340
+ <!-- hydrated when visible -->
341
+ <my-footer lwr:hydrate="visible"></my-footer>
342
+ ```
343
+
344
+ Components with `lwr:hydrate="visible"` will:
345
+
346
+ - Be server-side rendered normally
347
+ - Have their hydration deferred until they enter the viewport
348
+ - Use the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) to detect visibility
349
+ - Automatically hydrate when they become visible with a 100px root margin buffer
350
+
351
+ #### Root Margin
352
+
353
+ The root margin determines how close a component needs to be to the viewport before it starts hydrating. Components will hydrate when they are 100px away from entering the viewport. This provides a buffer to ensure hydration completes before the user sees the component.
354
+
355
+ The root margin is currently set to a fixed value of `100px` in the LWR implementation and is not configurable.
356
+
357
+ This approach is particularly useful for:
358
+
359
+ - Below-the-fold content that users may not scroll to
360
+ - Large components that are expensive to hydrate
361
+ - Progressive enhancement of page interactivity
362
+
329
363
  ## Routing
330
364
 
331
365
  LWR provides a Server Router, which is similar to the [Client Router](../router/README.md). The Server Router is fully compatible with SSRed pages, including [islands](#islands). It:
@@ -76,7 +76,8 @@ function lwcSsrViewTransformer(options, {config, moduleBundler, resourceRegistry
76
76
  let propsAttr = "";
77
77
  if (hydrate) {
78
78
  const propsId = (0, import_utils.getPropsId)();
79
- propsAttr = ` ${import_utils.SSR_PROPS_ATTR}="${propsId}"`;
79
+ const hydrateAttr = props && props[import_shared_utils.HYDRATE_DIRECTIVE] ? ` ${import_shared_utils.HYDRATE_DIRECTIVE}="${props[import_shared_utils.HYDRATE_DIRECTIVE]}"` : "";
80
+ propsAttr = ` ${import_utils.SSR_PROPS_ATTR}="${propsId}"${hydrateAttr}`;
80
81
  serverData[propsId] = props;
81
82
  }
82
83
  const [, remain] = html.split(`<${tagName}`);
@@ -112,9 +113,12 @@ function getComponentsToSSR(customElements, stringBuilder) {
112
113
  if (!isCsr && !rawProps?.["lwc:external"] && location) {
113
114
  const {startOffset, endOffset} = location;
114
115
  const specifier = (0, import_shared_utils.kebabCaseToModuleSpecifier)(tagName);
115
- const hydrate = (0, import_shared_utils.isHydrateOnLoad)(rawProps);
116
+ const isHydrateOnVisible = (0, import_shared_utils.isHydrateVisible)(rawProps);
117
+ const hydrate = (0, import_shared_utils.isHydrateOnLoad)(rawProps) || isHydrateOnVisible;
116
118
  const props = {...rawProps};
117
- delete props[import_shared_utils.HYDRATE_DIRECTIVE];
119
+ if (!isHydrateOnVisible) {
120
+ delete props[import_shared_utils.HYDRATE_DIRECTIVE];
121
+ }
118
122
  cmpInfo[specifier] = {startOffset, endOffset, props, tagName, specifier, hydrate};
119
123
  }
120
124
  }
@@ -1,6 +1,6 @@
1
1
  import { descriptions, logger, LwrApplicationError, LwrError, stringifyError } from '@lwrjs/diagnostics';
2
2
  import { ViewSpan, getTracer } from '@lwrjs/instrumentation';
3
- import { HYDRATE_DIRECTIVE, addHeadMarkup, isCsrIsland, isHydrateOnLoad, kebabCaseToModuleSpecifier, shortestTtl, } from '@lwrjs/shared-utils';
3
+ import { HYDRATE_DIRECTIVE, addHeadMarkup, isCsrIsland, isHydrateOnLoad, isHydrateVisible, kebabCaseToModuleSpecifier, shortestTtl, } from '@lwrjs/shared-utils';
4
4
  import { SSR_PROPS_ATTR, getPropsId, mergeWarnings } from '../utils.js';
5
5
  import { getRenderer } from '../renderer.js';
6
6
  /**
@@ -68,7 +68,10 @@ export default function lwcSsrViewTransformer(options, { config, moduleBundler,
68
68
  if (hydrate) {
69
69
  // Only serialize props for custom elements that are to be hydrated
70
70
  const propsId = getPropsId();
71
- propsAttr = ` ${SSR_PROPS_ATTR}="${propsId}"`;
71
+ const hydrateAttr = props && props[HYDRATE_DIRECTIVE]
72
+ ? ` ${HYDRATE_DIRECTIVE}="${props[HYDRATE_DIRECTIVE]}"`
73
+ : '';
74
+ propsAttr = ` ${SSR_PROPS_ATTR}="${propsId}"${hydrateAttr}`;
72
75
  serverData[propsId] = props;
73
76
  }
74
77
  const [, remain] = html.split(`<${tagName}`);
@@ -110,9 +113,13 @@ function getComponentsToSSR(customElements, stringBuilder) {
110
113
  // Only SSR the custom elements which are NOT CSR islands or lwc:external
111
114
  const { startOffset, endOffset } = location;
112
115
  const specifier = kebabCaseToModuleSpecifier(tagName);
113
- const hydrate = isHydrateOnLoad(rawProps);
116
+ const isHydrateOnVisible = isHydrateVisible(rawProps);
117
+ const hydrate = isHydrateOnLoad(rawProps) || isHydrateOnVisible;
114
118
  const props = { ...rawProps };
115
- delete props[HYDRATE_DIRECTIVE];
119
+ // We need to keep the directive for islands that will be hydrated when visibile
120
+ if (!isHydrateOnVisible) {
121
+ delete props[HYDRATE_DIRECTIVE];
122
+ }
116
123
  cmpInfo[specifier] = { startOffset, endOffset, props, tagName, specifier, hydrate };
117
124
  }
118
125
  }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "0.20.1",
7
+ "version": "0.20.2",
8
8
  "homepage": "https://developer.salesforce.com/docs/platform/lwr/overview",
9
9
  "repository": {
10
10
  "type": "git",
@@ -48,11 +48,11 @@
48
48
  "package.cjs"
49
49
  ],
50
50
  "dependencies": {
51
- "@lwrjs/config": "0.20.1",
52
- "@lwrjs/diagnostics": "0.20.1",
53
- "@lwrjs/instrumentation": "0.20.1",
54
- "@lwrjs/loader": "0.20.1",
55
- "@lwrjs/shared-utils": "0.20.1",
51
+ "@lwrjs/config": "0.20.2",
52
+ "@lwrjs/diagnostics": "0.20.2",
53
+ "@lwrjs/instrumentation": "0.20.2",
54
+ "@lwrjs/loader": "0.20.2",
55
+ "@lwrjs/shared-utils": "0.20.2",
56
56
  "@rollup/plugin-node-resolve": "^15.2.3",
57
57
  "@rollup/plugin-terser": "^0.4.4",
58
58
  "fs-extra": "^11.2.0",
@@ -61,7 +61,7 @@
61
61
  "undici": "^6.21.2"
62
62
  },
63
63
  "devDependencies": {
64
- "@lwrjs/types": "0.20.1",
64
+ "@lwrjs/types": "0.20.2",
65
65
  "jest": "29.7.0",
66
66
  "memfs": "^4.13.0",
67
67
  "ts-jest": "^29.2.6"
@@ -75,5 +75,5 @@
75
75
  "volta": {
76
76
  "extends": "../../../package.json"
77
77
  },
78
- "gitHead": "0621a79cb4f645af3500d6ba81ad561cf98ae944"
78
+ "gitHead": "be82aca54f9a3b6cd18a4aac86f2f96c0184a930"
79
79
  }