Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[labs/ssr-react] Add package for integrating Lit SSR to React #3605

Merged
merged 15 commits into from
Feb 8, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/wild-boxes-chew.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@lit-labs/ssr-react': minor
---

Initial release of `@lit-labs/ssr-react` package.

This package contains tools to deeply server render Lit components being used in React projects.
8 changes: 8 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,14 @@ packages/labs/ssr-client/index.*

packages/labs/ssr-dom-shim/index.*

packages/labs/ssr-react/node/
packages/labs/ssr-react/lib/
packages/labs/ssr-react/test/
packages/labs/ssr-react/index.*
packages/labs/ssr-react/jsx-runtime.*
packages/labs/ssr-react/jsx-dev-runtime.*
packages/labs/ssr-react/enable-lit-ssr.*

packages/labs/task/development/
packages/labs/task/test/
packages/labs/task/node_modules/
Expand Down
8 changes: 8 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,14 @@ packages/labs/ssr-client/index.*

packages/labs/ssr-dom-shim/index.*

packages/labs/ssr-react/node/
packages/labs/ssr-react/lib/
packages/labs/ssr-react/test/
packages/labs/ssr-react/index.*
packages/labs/ssr-react/jsx-runtime.*
packages/labs/ssr-react/jsx-dev-runtime.*
packages/labs/ssr-react/enable-lit-ssr.*

packages/labs/task/development/
packages/labs/task/test/
packages/labs/task/node_modules/
Expand Down
168 changes: 168 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"./packages/labs/ssr:build",
"./packages/labs/ssr-client:build",
"./packages/labs/ssr-dom-shim:build",
"./packages/labs/ssr-react:build",
"./packages/labs/task:build",
"./packages/labs/testing:build",
"./packages/labs/virtualizer:build",
Expand Down Expand Up @@ -96,6 +97,7 @@
"./packages/labs/ssr:build:ts",
"./packages/labs/ssr-client:build:ts",
"./packages/labs/ssr-dom-shim:build:ts",
"./packages/labs/ssr-react:build:ts",
"./packages/labs/task:build:ts",
"./packages/labs/testing:build:ts",
"./packages/labs/virtualizer:build:ts",
Expand Down Expand Up @@ -140,6 +142,7 @@
"./packages/labs/gen-wrapper-react:test",
"./packages/labs/gen-wrapper-vue:test",
"./packages/labs/ssr:test",
"./packages/labs/ssr-react:test",
"./packages/labs/testing:test",
"./packages/labs/virtualizer:test"
]
Expand Down
7 changes: 7 additions & 0 deletions packages/labs/ssr-react/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/node/
/lib/
/test/
/index.*
/jsx-runtime.*
/jsx-dev-runtime.*
/enable-lit-ssr.*
94 changes: 94 additions & 0 deletions packages/labs/ssr-react/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# @lit-labs/ssr-react

A package for integrating Lit SSR with React and React frameworks.

## Overview

By default, React's SSR library renders custom elements _shallowly_, i.e. only the element's open and closing tags, attributes, and light DOM children are present in the server-rendered HTML - shadow DOM contents are not rendered.

This package provides tools to integrate [`@lit-labs/ssr`](../ssr/README.md) with React SSR so that Lit components are deeply rendered, including their shadow DOM contents.

## Usage

To get React SSR to call into our library, we need to patch or replace React's default `createElement()` function and/or runtime JSX functions with our own. The way to achieve this depends on your project configuration.
augustjk marked this conversation as resolved.
Show resolved Hide resolved

### Using the Classic Runtime JSX Transform

The classic JSX transform replaces JSX expressions with `React.createElement()` function calls. In the default mode, it requires that `React` is imported and available in the scope of the JSX file.

This package provides a couple different ways to handle the classic runtime:

#### Monkey patching `React.createElement()` (recommended)

This package provides a module that, when imported in a server environment, has the side-effect of monkey patching `React.createElement()` to be an enhanced to add the declarative shadow DOM output to registered custom elements. This can be imported at the entry point of the application before `React` is imported.

```js
// index.js
import '@lit-labs/ssr-react/enable-lit-ssr.js';

import React from 'react';
import ReactDOM from 'react-dom';

...
```

In the browser environment, this module does not patch `React.createElement()` but instead imports `lit/experimental-hydrate-support.js` which must be imported before the `lit` package to allow hydration of server-rendered Lit elements.

This approach also has the advantage of working on Lit components turned to React components with the `@lit-labs/react` package which calls `React.createElement()` directly. It'll also work for any external React components pre-compiled with the classic JSX runtime transform.
augustjk marked this conversation as resolved.
Show resolved Hide resolved

#### Specifying an alternative `createElement()` function

If you wish to control which components use the enhanced `createElement()` function without a global monkey patch, you may do so by using a JSX pragma.

```diff
- import React from 'react';
+ /** @jsx createElement */
+ import {createElement} from '@lit-labs/ssr-react';

const Component = (props) => {
return <my-element />;
}
```
augustjk marked this conversation as resolved.
Show resolved Hide resolved

You may also set the compiler options to specify the function to use instead of the JSX pragma.

- For Babel: set the [`pragma`](https://babeljs.io/docs/en/babel-preset-react#pragma) option for `@babel/preset-react` to `"createElement"`.
- For TypeScript: set the [`jsxFactory`](https://www.typescriptlang.org/tsconfig#jsxFactory) option in `tsconfig.json` to `"createElement"`.

Note that the import line must still be present for every file that contains JSX expressions to transform in the classic runtime mode.

This approach only works for server-rendering custom elements added to the project in JSX expressions. It will not affect any pre-compiled JSX expressions or direct calls to `React.createElement()`. You will also need to manually import the `lit/experimental-hydrate-support.js` to your client JS. For those scenarios, use the [monkey patching](#monkey-patching-reactcreateelement-recommended) approach.

### Using the Automatic Runtime JSX Transform

If your project is using the [runtime JSX transform](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html), this package can serve as the JSX import source.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a solution at all for projects that use dependencies compiled with the runtime JSX transform? Do we need to note this case?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be a very unlikely use case that affects external React components pre-compiled with auto runtime transform that directly make use of Lit components without the labs/react wrapper. But added a couple sentences to the bottom of this section regarding that use case.


- For Babel: set the [`importSource`](https://babeljs.io/docs/en/babel-preset-react#importsource) option in `@babel/preset-react` to `@lit-labs/ssr-react`.
- For TypeScript: set the [`jsxImportSource`](https://www.typescriptlang.org/tsconfig#jsxImportSource) option in `tsconfig.json` to `@lit-labs/ssr-react`.

These JSX runtime modules contain jsx functions enhanced to add the declarative shadow DOM output to registered custom elements when imported into server environemtns. They also automatically import `lit/experimental-hydrate-support.js` in the browser environment.

However, they will not work for any pre-compiled JSX expressions or direct calls to `React.createElement()`, including those in the usage of the `@lit-labs/react` package's `createElement()`. Consider combining this with the [monkey patching](#monkey-patching-reactcreateelement-recommended) approach to handle such scenarios.

### Advanced Usage

For composing multiple `createElement()` functions, e.g. for use along side other React libraries that enhancee `createElement()`, this package also provides a `wrapCreateElement()` function which accepts a `createElement()` function and returns an enhanced one.

```js
import {wrapCreateElement} from '@lit-labs/ssr-react';
import React from 'react';

const enhancedCreateElement = wrapCreateElement(React.createElement);
```

## How it Works

The enhancements to `React.createElement()` or runtime JSX functions work by adding a `<template shadowrootmode>` element to the custom element's `children` list, if the custom element is defined and has a Lit SSR `ElementRenderer` registered to SSR the element. By default, all `LitElement` subclasses are rendered by the built-in `LitElementRenderer`.

## Enabling Declarative Shadow DOM

As of February 2023, declarative shadow DOM is supported in Chromium and Safari Technology Preview. For browsers that do not yet support it, you must include the [`template-shadowroot`](https://github.com/webcomponents/template-shadowroot) polyfill. Without this, React's own hydration may warn of hydration mismatch due to the lingering `<template>` element. See https://lit.dev/docs/ssr/client-usage/#using-the-template-shadowroot-polyfill for inspiration on how to incorporate this into your application.

## Contributing

Please see [CONTRIBUTING.md](../../../CONTRIBUTING.md).