A React component wrapper for web components that has graduated from labs and now serves as a proxy to @lit/react
npx @tessl/cli install tessl/npm-lit-labs--react@2.1.00
# @lit-labs/react
1
2
React integration for Web Components and Reactive Controllers. This package has graduated from labs and now serves as a proxy that re-exports functionality from `@lit/react` while maintaining compatibility during the ecosystem migration.
3
4
## Package Information
5
6
- **Package Name**: @lit-labs/react
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Installation**: `npm install @lit-labs/react`
10
11
## Core Imports
12
13
```typescript
14
import { createComponent } from "@lit-labs/react";
15
import { useController } from "@lit-labs/react/use-controller.js";
16
```
17
18
For CommonJS:
19
20
```javascript
21
const { createComponent } = require("@lit-labs/react");
22
const { useController } = require("@lit-labs/react/use-controller.js");
23
```
24
25
## Basic Usage
26
27
```typescript
28
import * as React from 'react';
29
import { createComponent } from '@lit-labs/react';
30
import { MyElement } from './my-element.js';
31
32
// Create React component wrapper for custom element
33
export const MyElementComponent = createComponent({
34
tagName: 'my-element',
35
elementClass: MyElement,
36
react: React,
37
events: {
38
onactivate: 'activate',
39
onchange: 'change',
40
},
41
});
42
43
// Use the component in JSX
44
function App() {
45
const [isActive, setIsActive] = React.useState(false);
46
47
return (
48
<MyElementComponent
49
active={isActive}
50
onactivate={(e) => setIsActive(e.active)}
51
onchange={(e) => console.log('Changed:', e.detail)}
52
/>
53
);
54
}
55
```
56
57
## Architecture
58
59
@lit-labs/react provides two main utilities:
60
61
- **createComponent**: Creates React component wrappers for custom elements that correctly handle property passing and event handling
62
- **useController**: React hook that allows using Reactive Controllers within React components with proper lifecycle integration
63
- **Type System**: Full TypeScript support with generic types for custom elements and events
64
- **Proxy Pattern**: All functionality is re-exported from `@lit/react` to maintain compatibility during migration
65
66
## Capabilities
67
68
### Component Creation
69
70
Creates React component wrappers for custom elements with proper property and event handling.
71
72
```typescript { .api }
73
/**
74
* Creates a React component for a custom element
75
* @param options Configuration object for the wrapper component
76
* @returns React component that wraps the custom element
77
*/
78
function createComponent<I extends HTMLElement, E extends EventNames = {}>(
79
options: Options<I, E>
80
): ReactWebComponent<I, E>;
81
82
interface Options<I extends HTMLElement, E extends EventNames = {}> {
83
/** The React module, typically imported from the 'react' npm package */
84
react: typeof React;
85
/** The custom element tag name registered via customElements.define */
86
tagName: string;
87
/** The custom element class registered via customElements.define */
88
elementClass: Constructor<I>;
89
/** Object mapping React prop names to custom element event names */
90
events?: E;
91
/** React component display name, used in debugging messages */
92
displayName?: string;
93
}
94
95
type Constructor<T> = { new (): T };
96
97
type EventNames = Record<string, EventName | string>;
98
```
99
100
**Usage Examples:**
101
102
```typescript
103
import * as React from 'react';
104
import { createComponent } from '@lit-labs/react';
105
import { MyElement } from './my-element.js';
106
107
// Basic component creation
108
const MyElementComponent = createComponent({
109
tagName: 'my-element',
110
elementClass: MyElement,
111
react: React,
112
});
113
114
// Component with event mapping
115
const InteractiveComponent = createComponent({
116
tagName: 'interactive-element',
117
elementClass: InteractiveElement,
118
react: React,
119
events: {
120
onactivate: 'activate',
121
onchange: 'change',
122
onclick: 'click',
123
},
124
displayName: 'InteractiveElement',
125
});
126
```
127
128
### Controller Integration
129
130
React hook for using Reactive Controllers within React components.
131
132
```typescript { .api }
133
/**
134
* Creates and stores a stateful ReactiveController instance with React lifecycle
135
* @param React The React module that provides the base hooks (useState, useLayoutEffect)
136
* @param createController Function that creates a controller instance given a host
137
* @returns The controller instance
138
*/
139
function useController<C extends ReactiveController>(
140
React: typeof window.React,
141
createController: (host: ReactiveControllerHost) => C
142
): C;
143
144
type ControllerConstructor<C extends ReactiveController> = {
145
new (...args: Array<any>): C;
146
};
147
```
148
149
**Usage Examples:**
150
151
```typescript
152
import * as React from 'react';
153
import { useController } from '@lit-labs/react/use-controller.js';
154
import { MouseController } from '@example/mouse-controller';
155
156
// Create a React hook using a controller
157
const useMouse = () => {
158
const controller = useController(React, (host) => new MouseController(host));
159
return controller.position;
160
};
161
162
// Use the controller hook in a React component
163
const Component = () => {
164
const mousePosition = useMouse();
165
return (
166
<pre>
167
x: {mousePosition.x}
168
y: {mousePosition.y}
169
</pre>
170
);
171
};
172
173
// Direct controller usage
174
const MyComponent = () => {
175
const myController = useController(React, (host) => new MyCustomController(host));
176
177
return (
178
<div>
179
<p>Controller state: {myController.value}</p>
180
<button onClick={() => myController.increment()}>
181
Increment
182
</button>
183
</div>
184
);
185
};
186
```
187
188
## Types
189
190
```typescript { .api }
191
/**
192
* Type used to cast event names with specific event types for better typing
193
*/
194
type EventName<T extends Event = Event> = string & {
195
__eventType: T;
196
};
197
198
/**
199
* Type of the React component wrapping the web component
200
*/
201
type ReactWebComponent<
202
I extends HTMLElement,
203
E extends EventNames = {}
204
> = React.ForwardRefExoticComponent<
205
React.HTMLAttributes<I> & Partial<Omit<I, keyof HTMLElement>> & React.RefAttributes<I>
206
>;
207
208
/**
209
* Creates a type for props of a web component used directly in React JSX
210
*/
211
type WebComponentProps<I extends HTMLElement> = React.DetailedHTMLProps<
212
React.HTMLAttributes<I>,
213
I
214
>;
215
216
/**
217
* Type for constructor functions that create ReactiveController instances
218
*/
219
type ControllerConstructor<C extends ReactiveController> = {
220
new (...args: Array<any>): C;
221
};
222
223
/**
224
* External types from @lit/reactive-element (not part of @lit-labs/react API)
225
* These interfaces are imported when using useController
226
*/
227
interface ReactiveController {
228
hostConnected?(): void;
229
hostDisconnected?(): void;
230
hostUpdate?(): void;
231
hostUpdated?(): void;
232
}
233
234
interface ReactiveControllerHost {
235
addController(controller: ReactiveController): void;
236
removeController(controller: ReactiveController): void;
237
requestUpdate(): void;
238
readonly updateComplete: Promise<boolean>;
239
}
240
```
241
242
## Advanced Usage
243
244
### Event Type Casting
245
246
```typescript
247
import type { EventName } from '@lit-labs/react';
248
249
// Define typed events for better type safety
250
const MyElementComponent = createComponent({
251
tagName: 'my-element',
252
elementClass: MyElement,
253
react: React,
254
events: {
255
onClick: 'pointerdown' as EventName<PointerEvent>,
256
onChange: 'input' as EventName<InputEvent>,
257
},
258
});
259
260
// Now event callbacks are properly typed
261
<MyElementComponent
262
onClick={(e: PointerEvent) => console.log('Pointer event:', e)}
263
onChange={(e: InputEvent) => console.log('Input event:', e)}
264
/>
265
```
266
267
### JSX Declaration
268
269
```typescript
270
declare module 'react' {
271
namespace JSX {
272
interface IntrinsicElements {
273
'my-element': WebComponentProps<MyElement>;
274
}
275
}
276
}
277
278
// Now you can use the element directly in JSX
279
<my-element customProp={value} onCustomEvent={handler} />
280
```
281
282
## Error Handling
283
284
- **Missing Element Class**: `createComponent` will warn if reserved React properties are found on the element prototype
285
- **Event Handler Errors**: Event listeners are automatically managed; errors in handlers should be caught by the consuming application
286
- **Controller Lifecycle**: `useController` handles React Strict Mode correctly and manages controller lifecycle automatically
287
288
## Server-Side Rendering
289
290
Components created with `createComponent` support server-side rendering when used with `@lit/ssr-react`. The wrapper handles:
291
292
- Adding `defer-hydration` attribute to prevent hydration warnings
293
- Passing element properties through `_$litProps$` for server rendering
294
- Automatic hydration suppression warning management
295
296
```typescript
297
// SSR usage (works automatically with @lit/ssr-react)
298
const ServerRenderedComponent = createComponent({
299
tagName: 'my-element',
300
elementClass: MyElement,
301
react: React,
302
});
303
304
// Component will render properly on server and hydrate correctly on client
305
<ServerRenderedComponent customProp={value} />
306
```
307
308
## Migration Notes
309
310
This package is deprecated and serves as a compatibility layer. For new projects, use `@lit/react` directly:
311
312
```typescript
313
// Old (deprecated but still works)
314
import { createComponent } from '@lit-labs/react';
315
316
// New (recommended)
317
import { createComponent } from '@lit/react';
318
```